<template>
  <ie-page>

    <template #extra>
      <n-space>
        <n-button type="primary" @click="handleInsert()">新增根组织</n-button>
      </n-space>
    </template>

    <div class="ie-table">
      <a-tree
        :data="treeData"
        size="large"
        block-node
      >
        <template #extra="nodeData">
          <a-space>
            <a-button status="success" size="mini" @click="handleInsert(nodeData)">新增</a-button>
            <a-button status="warning" size="mini" @click="handleUpdate(nodeData)">修改</a-button>
            <a-popconfirm
              content="删除将会同时移除其子节点，请确定是否删除？"
              position="tr"
              @before-ok="done => handleRemove(nodeData, done)"
            >
              <a-button status="danger" size="mini">删除</a-button>
            </a-popconfirm>
          </a-space>
        </template>
      </a-tree>
    </div>

    <a-modal
      v-model:visible="visible"
      :title="formValue.id ? '修改组织' : '新增组织'"
      title-align="start"
      @before-ok="handleOk"
      @ok="handleCancel"
      @cancel="handleCancel"
    >
      <a-form
        :model="formValue"
        @submit="handleSubmit"
        layout="vertical"
        size="large"
        ref="formRef"
      >
        <a-form-item
          label="部门名称"
          field="name"
          :rules="[
            { required: true, message: '组织名称不可为空' }
          ]"
          :validate-trigger="['change', 'input', 'blur']"
        >
          <a-input
            v-model="formValue.name"
            placeholder="请输入组织名称"
            allow-clear
          />
          <template v-if="!formValue.id && formValue.parent_id" #extra>
            <div>在 {{ parentNodeRef.title }} 下增加子节点</div>
          </template>
        </a-form-item>
      </a-form>
    </a-modal>
  </ie-page>
</template>

<script setup>
import { computed, ref } from 'vue'
import { useQuery, useMutation } from 'villus'
import { cloneDeep } from 'lodash-es'
import gql from 'graphql-tag'

import { makeUpTree, findTreeNode, noop } from '/@/utils/helper'

const treeData = ref([])

useQuery({
  query: gql`
    query {
      department {
        id
        name
        parent_id
      }
    }
  `
}).then(({ data, error }) => {
  if (!error.value) {
    treeData.value = makeUpTree(data.value.department)
  }
})

const { execute: insert } = useMutation(gql`
  mutation ($object: department_insert_input!) {
    insert_department (
      objects: [$object]
    ) {
      returning {
        id
        name
        parent_id
      }
    }
  }
`)
const { execute: update } = useMutation(gql`
  mutation ($id: Int!, $name: String!) {
    update_department_by_pk (
      pk_columns: { id: $id },
      _set: { name: $name }
    ) {
      id
      name
      parent_id
    }
  }
`)
const { execute: remove } = useMutation(gql`
  mutation ($id: Int!) {
    delete_department_by_pk (id: $id) {
      id
    }
  }
`)

const visible = ref(false)
const formRef = ref()
const formInitValue = {
  id: null,
  parent_id: null,
  name: ''
}
const formValue = ref(cloneDeep(formInitValue))
const parentNodeRef = computed(() => {
  return formValue.value.parent_id
    && findTreeNode(treeData.value, formValue.value.parent_id)
})

function handleInsert(nodeData) {
  const parent_id = nodeData?.key

  if (parent_id) {
    formValue.value.parent_id = parent_id
  }

  visible.value = true
}

function handleUpdate(nodeData) {
  const id = nodeData?.key

  if (id) {
    formValue.value.id = id
    formValue.value.name = nodeData?.title
  }

  visible.value = true
}

function handleRemove(nodeData, done = noop) {
  const { key: id, parent_id } = nodeData || {}

  if (id) {
    return remove({ id }).then(res => {
      if (!res.errors) {
        let children

        if (parent_id) {
          children = findTreeNode(treeData.value, parent_id)?.children || []
        } else {
          children = treeData.value
        }

        if (Array.isArray(children)) {
          const index = children.findIndex(item => item.key === id)

          if (index > -1) {
            children.splice(index, 1)
          }
        }
      }
      done()
    })
  }
  done()
}

function handleSubmit({ values, errors }, done = noop) {
  if (!errors) {
    function callback(res) {
      if (!res.errors) {
        const data = res.data?.insert_department?.returning ||
          [res.data?.update_department_by_pk || {}]
        const [{ id, parent_id, name }] = data
        let children

        if (parent_id) {
          const parentNode = findTreeNode(treeData.value, parent_id)

          parentNode.children = parentNode.children || []
          children = parentNode.children
        } else if (id) {
          children = treeData.value
        }

        if (Array.isArray(children)) {
          const index = children.findIndex(item => item.key === id)

          if (index > -1) {
            children[index] = {
              ...children[index],
              title: name
            }
          } else {
            children.push({
              title: name,
              key: id,
              parent_id
            })
          }
        }
      }
      done()
    }

    return values.id
      ? update({
          id: values.id,
          name: values.name
        }).then(callback)
      : insert({ object: {
          parent_id: values.parent_id,
          name: values.name
        }}).then(callback)
  }

  done(false)
}

function handleCancel() {
  if (formRef.value?.resetFields) {
    formRef.value?.resetFields()
  }
  formValue.value = cloneDeep(formInitValue)
}

function handleOk(done) {
  if (formRef.value?.handleSubmit) {
    formRef.value?.handleSubmit(done)
  }
}
</script>